
// This file contains common code for finding functions in the context.
// Other finders might be needed, and these might not be needed, but
// the finders here cover the common cases for loading rio code over
// the net, along with some support for file systems.
//
// TODO Make this standalone from common?
//
// TODO Support hg revisions (and tags?).
// TODO Support bulk hg downloads for efficiency?


(function(){


// Namespace.

var rio = this.rio;
if (!rio) {
  rio = this.rio = {};
}


// Imports. TODO Make standalone?

var each = rio.each;


// Exports.

rio.listLib = listLib;
rio.load = load;


// Functions.

function absoluteUri(base, rel) {
  var abs;
  if (serverUri(rel)) {
    // It's fully absolute already.
    abs = rel;
  } else if (rel.match(/^\//)) {
    // It's subserver absolute. Find the server base.
    // TODO Support file uris (and others, too?).
    var server = serverUri(base);
    if (!server) {
      throw "no server for " + base;
    }
    abs = server + rel;
  } else {
    // It's fully relative.
    if (!base.match(/\/$/)) {
      base = base.replace(/[^\/]+$/, '');
    }
    var abs = base + rel;
  }
  abs = cleanUri(abs);
  return abs;
}

function cleanUri(uri) {
  var server = serverUri(uri);
  if (!server) {
    throw "no server for " + base;
  }
  uri = uri.substring(server.length);
  // Eliminate '/./'.
  uri = uri.replace(/\/\.(?:\/|$)/g, '/');
  // Eliminate duplicate slashes.
  uri = uri.replace(/\/{2,}/g, '/');
  // Eliminate '..'.
  while (uri.match(/\/\.\.(?:\/|$)/)) {
    // Remove leading '..'
    uri = uri.replace(/^\/\.\.(?:\/|$)/, '/');
    // Remove internal '..'.
    uri = uri.replace(
      // Says:
      // 2 not starting with dot or
      // 2 not ending with dot or
      // 1 of any or
      // 3 or more, then
      // followed by '/..' or '/../'.
      /\/(?:[^.\/][^\/]|[^\/][^.\/]|.|[^\/]{3,})\/\.\.(?:\/|$)/,
      '/'
    );
  }
  return server + uri;
}

/**
 * Loads entries for defs, whether from a dir or a lib script or both (for dir-lib.rio).
 */
function extractScripts(uri, text) {
  var scripts = [];
  var absParent = absoluteUri(window.location.toString(), uri);
  // TODO Support arbitrary resource loading, not just rio scripts.
  // TODO That means I'll need better parsing logic, maybe including the now deleted (see old revs!) href handling.
  // TODO Just remember default hg serving sends plain text lists for dirs, not HTML with hrefs. Any common patterns to such cases? Or just hard-code each?
  var scriptFiles = text.match(/\b[A-Za-z_][A-Za-z0-9_]*\.rio/g);
  var scriptFileSet = {};
  each(scriptFiles, function(scriptFile) {
    if (scriptFileSet[scriptFile]) {
      // Avoid duplicates.
      return;
    }
    scriptFileSet[scriptFile] = true;
    var name = /^(.*)\.rio$/.exec(scriptFile)[1];
    var script = {exports: {}, uri: absParent + scriptFile}
    script.exports[name] = {defs: {}, name: name};
    scripts.push(script);
  });
  return scripts;
}

function listLib(uri, handler) {
  load(uri, function(text) {
    var scripts = extractScripts(uri, text);
    handler(scripts);
  });
}

function load(uri, onReady, onError) {
  // TODO Support IE <= 6?
  var req = new XMLHttpRequest();
  req.open('GET', uri, true);
  req.onreadystatechange = function(event) {
    if (req.readyState == 4) {
      if(req.status == 200) {
        onReady(req.responseText, uri);
      } else {
        if (onError) onError(req.status, uri);
      }
    }
  };
  req.send(null);
}

function serverUri(uri) {
  var server = uri.match(/^http(?:s)?:\/\/[^\/]+/);
  return server ? server[0] : server;
}


})();
